package ace.cmp.spring.data.jdbc.querydsl.core.repository.handler.impl;

import ace.cmp.spring.data.jdbc.querydsl.core.annotations.LogicDelete;
import ace.cmp.spring.data.jdbc.querydsl.core.model.PropertyRelation;
import ace.cmp.spring.data.jdbc.querydsl.core.repository.handler.QuerydslLogicDeleteAnnotationHandler;
import ace.cmp.spring.data.jdbc.querydsl.core.repository.handler.QuerydslLogicDeleteGenerator;
import ace.cmp.spring.data.jdbc.querydsl.core.util.QuerydslPropertyRelationUtils;
import com.querydsl.core.types.Expression;
import com.querydsl.core.types.Path;
import com.querydsl.sql.dml.SQLInsertClause;
import com.querydsl.sql.dml.SQLUpdateClause;
import java.util.List;
import java.util.stream.Collectors;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.util.Assert;

/**
 * @author caspar
 * @date 2024/6/26 16:03
 */
public class QuerydslLogicDeleteAnnotationHandlerImpl
    implements QuerydslLogicDeleteAnnotationHandler {

  private final QuerydslPropertyRelationUtils querydslPropertyRelationUtils;
  private final QuerydslLogicDeleteGenerator querydslLogicDeleteGenerator;

  public QuerydslLogicDeleteAnnotationHandlerImpl(
      QuerydslPropertyRelationUtils querydslPropertyRelationUtils,
      QuerydslLogicDeleteGenerator querydslLogicDeleteGenerator) {
    this.querydslPropertyRelationUtils = querydslPropertyRelationUtils;
    this.querydslLogicDeleteGenerator = querydslLogicDeleteGenerator;
  }

  @Override
  public void processInsert(
      Object entity, SQLInsertClause sqlClause, boolean enableLogicDeleteForce) {
    List<PropertyRelation> propertyRelationList = this.getPropertyRelationList(entity.getClass());
    if (CollectionUtils.isEmpty(propertyRelationList)) {
      return;
    }

    for (PropertyRelation propertyRelation : propertyRelationList) {
      Object submitValue = querydslPropertyRelationUtils.getValue(entity, propertyRelation);
      Object generateValueNotDelete =
          querydslLogicDeleteGenerator.generateNewValueNotDelete(
              entity.getClass(), propertyRelation);
      Assert.notNull(
          generateValueNotDelete,
          String.format(
              "logic delete value type is not support,name:%s,type:%s",
              propertyRelation.getField().getName(),
              propertyRelation.getField().getGenericType().getTypeName()));
      Object value = submitValue;
      if (submitValue == null || enableLogicDeleteForce) {
        value = generateValueNotDelete;
      }
      querydslPropertyRelationUtils.setValue(entity, propertyRelation, value);
      sqlClause.set((Path) propertyRelation.getPath(), value);
    }
  }

  @Override
  public void processLogicDelete(Expression idExpression, Class entityClass, SQLUpdateClause sqlClause) {
    List<PropertyRelation> propertyRelationList = this.getPropertyRelationList(entityClass);
    if (CollectionUtils.isEmpty(propertyRelationList)) {
      return;
    }

    for (PropertyRelation propertyRelation : propertyRelationList) {
      Expression<?> newExpression =
          querydslLogicDeleteGenerator.generateNewExpression(
              idExpression,
              entityClass,
              propertyRelation
          );

      Assert.notNull(
          newExpression,
          String.format(
              "logic delete value type is not support,name:%s,type:%s",
              propertyRelation.getField().getName(),
              propertyRelation.getField().getGenericType().getTypeName()));
      sqlClause.set(propertyRelation.getPath(), (Expression) newExpression);
    }
  }

  @Override
  public void processUpdate(
      Object entity, SQLUpdateClause sqlClause, boolean enableLogicDeleteForce) {
    // 强制框架处理,则直接忽略
    if (enableLogicDeleteForce) {
      return;
    }
    this.getPropertyRelationList(entity.getClass())
        .forEach(
            propertyRelation -> {
              Object submitValue = querydslPropertyRelationUtils.getValue(entity, propertyRelation);
              if (submitValue == null) {
                return;
              }
              sqlClause.set((Path<Object>) propertyRelation.getPath(), submitValue);
            });
  }

  private List<PropertyRelation> getPropertyRelationList(Class entityClass) {
    return this.querydslPropertyRelationUtils
        .getPropertyRelation(entityClass, LogicDelete.class)
        .values()
        .stream()
        .collect(Collectors.toList());
  }
}
